'''
Created on May 27, 2010

@author: hide1713
'''
import sys
import os
import re
import logging
logging.basicConfig(level=logging.DEBUG)
import web
from web.contrib.auth import DBAuth
from parse_pathway import *


############################################
#Import base modules
############################################
try:
    from genome_config import GenomeConfig
    config = GenomeConfig()
    if os.path.exists("../default.cfg"):
        config.read_file("../default.cfg")
    else:
        print "Can not find config file in  ~/.ma_tool.cfg or ./default.cfg"
        print "Please start the program in source code directory or copy the default.cfg to ~/.ma_tool.cfg"
        sys.exit(1)
except ImportError:
    print "Can not locate source code folder. Check your installation or run this program in source code folder"
    sys.exit(1)

#URL mapping configuration.
#Note: the image files are stored in templates folder.
urls = (
    '/new_user/', 'NewUser',
    '/images/(.*)','Images',
    '/pathrecruit/(.*)','PathRecruit',
    '/familyrecruit/(.*)','FamilyRecruit',
    '/upload/','Upload',
    '/','Index'
    )
#Load database and web page render
db = web.database(dbn='sqlite', db='../auth.db')
settings = {}
app = web.application(urls, globals(), autoreload=False)    
auth = DBAuth(app, db, **settings)
render_glob = {'auth':auth}
render = web.template.render('../templates/',base='base',globals=render_glob)

import matplotlib
from controller import MainWindowController
from data_model.model import Model

#wire up model view and controller
model = Model()
ctl = MainWindowController(model)

#load data file
ctl.load_cog_file(config.cog_file_path)
ctl.load_data_directory(config.genome_data_folder)
ctl.set_hitp_folder(config.hitp_data_folder)

class Index:
    "Index Page"
    def GET(self):
        return render.index(config.url_base)
    
class PathRecruit:
    def GET(self, name=None):
        """Process user input and Call pathrecruit.PathRecruit class to draw image
        name: user name from url. Check webpy url handling document for detail
        """
        if name:    
            user = auth.authenticate(name, config.default_password)
            if user:
                auth.login(user)
                upload = False
            else:
                return render.pathrecruit(None,error="Wrong project name",\
                                          path=config.url_base,show_upload=False)
        else:
            upload = True
            user = auth.getUser()    
        user_path,upload_files = self._get_user_files(user)
        if self._draw_file(web.input()):#We want to render a image
#            try:
            #Validate user input
            #upload_files is the complete list of user uploaded files
            #data_file is the name of file that we want to draw
            #method is a string in (reads, species, diversity)
            #draw is a string in (linear, proportion)
            data_file, method, draw = self._verify_input(user, web.input(),upload_files)
            #Start to process image
            img, html, title = self._process_get(user_path+data_file, method, draw)
            return render.pathrecruit(upload_files,img,html,title,\
                                      path=config.url_base,show_upload=upload)
#            except Exception, e:
#                raise e
#                return render.pathrecruit(upload_files,error="Error:" + str(e))
        else:#just display files
            return render.pathrecruit(upload_files,path = config.url_base,show_upload=upload)
        
    def _verify_input(self, user, input, user_files):
        "Verify user files and drawing method"
        if not user:
            raise Exception("User does not exist")
        try:
            data_file = input['data_file']
            method = input['method']
            draw = input['draw']
        except KeyError:
            raise Exception("Missing Parameter")
        if data_file in user_files:
            return data_file,method,draw 
        else:
            raise Exception("Data file does not exist")
        
    def _process_get(self,data_file,method,draw):
        "Draw image"
        if method == "diversity":
            f = open(data_file[:-3]+"spediv",'r')
        else:
            f = open(data_file, 'r')
        method_map = {'reads':mark_edge, 'species':mark_edge_spec, \
                      'diversity':mark_edge_by_div}
        draw_map = {'by_count':draw_image, 'by_proportion':draw_image_proportion}
        im = Image.open("../templates/map.png")
        #Mark image edge
        mark_set, next_method, max_val = method_map[method](f, title_map)
        assert(len(mark_set)>0 and max_val>0)
        #Draw color on edges
        img,html = draw_map[draw](im, mark_set, next_method, max_val)
        assert(len(html)>0)
        title = "%s %s %s"%(os.path.basename(data_file), method, draw)
        del im
        return img,html,title
            
    def _draw_file(self,input):
        "Test if user want to draw a file"
        if input.has_key('data_file') and input.has_key('method') and input.has_key('draw'):
            return True
        else:
            return False
    
    def _get_user_files(self,user):
        "Read user files from disk"
        uid = user.user_id
        user_path = config.genome_data_folder + str(uid)+"/"
        if not os.path.exists(user_path):
            logging.debug("Creating Dir:"+user_path)
            os.mkdir(user_path)
        user_files = [f for f in os.listdir(user_path) if f.endswith(".tax")]
        return user_path, user_files
    
    def _verify_upload(self,user,input):
        if not user:
            raise Exception("Must login as a user")
        if 'myfile' not in input:
            raise Exception("Upload file error")
        filepath=input.myfile.filename.replace('\\','/') # replaces the windows-style slashes with linux ones.
        filename=filepath.split('/')[-1] # splits the and chooses the last part (the filename with extension)
        if not filename.endswith('.tax') and not filename.endswith('.zip'):
            raise Exception("Wrong file format")
        return filename 
    
    def _call_div(self,tax_file):
        "Call pathdiv program to process tax file"
        cmd = "/dataomics/Sim2Tax/pathdiv -t /dataomics/Sim2Tax/data/kegg-ncbi.tre -a "
        cmd += tax_file
        spediv = tax_file[:-4]+".spediv"
        logging.info("Generating "+spediv)
        cmd += " -o "+spediv
        logging.debug(cmd)
        os.system(cmd)
        if not os.path.exists(spediv):
            raise Exception("Fail to generate spediv file")
        
    def unzip(self,file,user_dir):
        "Unzip the file and return the tax file names"
        extract_name = []
        import zipfile
        zip = zipfile.ZipFile(file,'r')
        if zip:
            for f in zip.namelist():
                if f.endswith(".tax") and not os.path.basename(f).startswith('.'):
                    output = user_dir+os.path.basename(f)
                    logging.info("Unzipping file to "+output)
                    fout = open(output,'w')
                    if fout:
                        fout.write(zip.read(f))
                        fout.close()
                        extract_name.append(output)
                    else:
                        logging.debug("open zip output file failed")
        else:
            logging.debug("Extract zip file failed")
        return extract_name
        
    def POST(self, name=None):
        "Process user uploaded file"
        x = web.input(myfile={})
        user = auth.getUser()
        if not user:
            return render.pathrecruit(None,error="Wrong user name",path=config.url_base,\
                                      is_project=False)
        user_path,files = self._get_user_files(user)
        try:
            file_name = self._verify_upload(auth.getUser(),x)
            print "upload filename"+file_name
            if file_name.endswith(".tax"):
                tax_file = user_path+file_name
                fout = open(tax_file,'w')
                fout.write(x.myfile.file.read())
                fout.close()
                self._call_div(tax_file)
            elif file_name.endswith('.zip'):
                extract = self.unzip(x.myfile.file,user_path)
                assert len(extract)>0, "Can not extract tax file from zip file"
                for tax_file in extract:
                    self._call_div(tax_file)
        except Exception,e:
            return render.pathrecruit(files,error=e,path=config.url_base)
        raise web.seeother('/pathrecruit/')
    
class Images:
    "Transfer Images from templates folder"
    def GET(self,name):
        ext = name.split(".")[-1] # Gather extension
        cType = {
            "png":"images/png",
            "jpg":"image/jpeg",
            "gif":"image/gif",
            "ico":"image/x-icon"            }
        if name in os.listdir('../templates'):  # Security
            web.header("Content-Type", cType[ext]) # Set the Header
            return open('../templates/%s'%name,"rb").read() # Notice 'rb' for reading images
        else:
            web.notfound()

class FamilyRecruit(object):
    """Family Recruit viewing class
    Process user input and render page accordingly
    """
    def GET(self,name=None):
        """Return a string of web page
        name: user name from url. Check webpy url handling document for detail
        """
        input = web.input(data_file=[])
        image = None
        cog = {}
        model = ctl.get_model()
        upload = True
        if name:
            #login default project.
            #The user name is in url. Eg. omics.informatics.indiana.edu/metavizs/familyrecruit/user
            #The default password can be found in config.default_passowrd
            u = auth.authenticate(name,config.default_password)
            if u:
                auth.login(u)
                logging.debug("Family Recruit Project "+name)
                upload = False
            else:
                return render.familyrecruit(image,cog,"Wrong user",config.url_base,False)
        user = auth.getUser()
        if not user:
            raise web.seeother(config.url_base+"/login/")
        files = model.get_child_model_names(user.user_id)
        if input.has_key("rm"):#Remove files
            if user:
                self._remove_file(input["data_file"],user.user_id)
                raise web.seeother('/familyrecruit/')
        elif input.has_key("sr"):#Draw Segment
            image = self._segment(input,user.user_id)
        elif input.has_key("cf"):#Draw Coverage
            image = self._coverage(input,user.user_id)
        elif input.has_key("fc"):#Draw Function
            image = self._function(input,user.user_id)
        #Display cog list
        for file in files:
            logging.debug("Read cog from file "+file)
            if user:
                cog_model = model.get_child_model(file,user.user_id)
            else:
                cog_model = model.get_child_model(file,None)
            cog[file]= cog_model.get_non_empty_cog_names()
            cog[file].sort()
        return render.familyrecruit(image,cog,show_upload=upload,path=config.url_base)
        
    def _segment(self,input,user_id):
        cog_file = ""
        cog_name = ""
        for i in input:
            if i != "data_file" and input[i].startswith("COG"):
                cog_file = i
                cog_name = input[i].split("|")[0]
                break
        if cog_file and cog_name:
            logging.debug("Drawing cog %s %s"%(cog_file,cog_name))
            return ctl.draw_segment([(cog_name,cog_file)],user_id)

    def _coverage(self,input,user_id):
        cog_file = ""
        cog_name = ""
        for i in input:
            if i != "data_file" and input[i].startswith("COG"):
                cog_file = i
                cog_name = input[i].split("|")[0]
                break
        if cog_file and cog_name:    
            logging.debug("Drawing coverage  %s %s"%(cog_file,cog_name))
            return ctl.draw_coverage([(cog_name,cog_file)],user_id)

    def _function(self,input,user_id):
        files = input["data_file"]
        return ctl.draw_function_category(files,user_id)

    def _remove_file(self,file_names,user_id):
        "Remove multiple files"
        file_dir = config.genome_data_folder
        for name in file_names:
            file_path = file_dir+"/"+str(user_id)+"/"+name
            if os.path.exists(file_path):
                db.delete('file',
                          where = 'file_user_id = $id and file_name = $name',
                          vars = {'id':user_id,'name':name})
                os.remove(file_path)
                ctl.remove_data_file(name,user_id)
            
class Upload:
    "File Uploading Page For Family Recruit"
    @auth.protected()
    def GET(self):
        return render.upload()
    
    @auth.protected()
    def POST(self):
        x = web.input(myfile={})
        user = auth.getUser()
        user_id = auth.getUser().user_id
        base_dir = config.genome_data_folder # change this to the directory you want to store files.
        user_dir = base_dir+str(user_id)+"/family"
        if not os.path.exists(user_dir):
            logging.debug("Creating Dir:"+user_dir)
            os.makedirs(user_dir)
        if 'myfile' in x and x.myfile.filename: # to check if the file-object is created
            filepath=x.myfile.filename.replace('\\','/') # replaces the windows-style slashes with linux ones.
            filename=filepath.split('/')[-1] # splits the and chooses the last part (the filename with extension)
            logging.debug("User: "+str(user_id)+" uploads "+filename)
            new_file = user_dir +'/'+ filename
            fout = open(new_file,'w') # creates the file where the uploaded file should be stored
            fout.write(x.myfile.file.read()) # writes the uploaded file to the newly created file.
            fout.close() # closes the file, upload complete.
            #Load model file
            try:
                ctl.load_data_file(user_dir+'/'+filename,user_id)
            except Exception,e:
                os.remove(new_file)
                return render.upload(error=str(e))
            raise web.seeother("/familyrecruit/")
        return render.upload(error="Empty upload file")

            
class NewUser:
    """Validate and Create New Users
    """
    def validateEmail(self,email):
        if len(email) > 7:
            if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", email) != None:
                return True
        return False

    def GET(self):
        return render.new_user()

    def POST(self):
        input = web.input()
        pass_size = len(input['password'])
        id_size = len(input['login'])
        if 6 > pass_size or pass_size > 32 or 6 > id_size or id_size > 32:
            return render.new_user(error = 'wrong_size')
        if input['password']!=input['repassword']:
            return render.new_user(error = 'missmatch')
        if not self.validateEmail(input['mail']):
            return render.new_user(error = 'mail')
        if auth.userExist(input["login"]):
            return render.new_user(error = "exists")
        mail = input['mail']
        auth.createUser(input['login'],input['password'], user_email = mail)
        raise web.seeother(config.url_base+'/')

if __name__ == '__main__':
    app.run()
